package es.ucm.fdi.xtremeaudio.modelo.parser;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import es.ucm.fdi.xtremeaudio.modelo.beans.Biblioteca;
import es.ucm.fdi.xtremeaudio.modelo.beans.Cancion;
import es.ucm.fdi.xtremeaudio.modelo.beans.Playlist;
import es.ucm.fdi.xtremeaudio.modelo.excepciones.ReaderException;

/**
 * <ul>
 * <li><b>Proyecto:</b> XtremeAudio</li>
 * <li><b>Grupo:</b> 7 <i>(4&deg;B)</i></li>
 * <li><b>Asignatura:</b> Ingenier&iacute;a del Software</li>
 * <li><b>Curso:</b> 2010/2011</li>
 * <li><b>Centro:</b> Universidad Complutense de Madrid</li>
 * </ul>
 * 
 * La clase <b>ParserSAXLista</b> se encarga de parsear el archivo <i>XML</i>
 * asociado a las listas de reproducci&oacute;n, de manera que si el archivo
 * existe y es correcto (respecto a la especifica&oacute;n indicada en el
 * archivo <i>lista.dtd</i>, en la carpeta &quot;conf&quot;), devolver&aacute;
 * el mapeado de dicho archivo en forma de objeto de la clase {@link Playlist}.
 * El parseo de las listas va asociado al de la biblioteca y se ejecuta al
 * arrancar la aplicaci&oacute;n. Esta clase es una extensi&oacute;n de la clase
 * {@link DefaultHandler}.
 * 
 * @author &Aacute;lvaro G&oacute;mez Porris
 * @author Elena Hern&aacute;ndez Delgado
 * @author Mar&iacute;a Trinidad Mart&iacute;n Campos
 * @author Mois&eacute;s Mart&iacute;nez Montes
 * @author Mat&iacute;as Salinero Delgado
 * 
 * @version %I%, %G%
 * 
 * @since 1.0
 * 
 * @see Biblioteca
 * @see Playlist
 */
public class ParserSAXLista extends DefaultHandler {

	/*
	 * Lista de ids de canciones resultado de un parseo
	 */
	private ArrayList<Integer> vIds = null;

	/*
	 * Cancion actualmente leido en un proceso de parseo
	 */
	private Integer idActual = null;

	/*
	 * Buffer de caracteres de un elemento en un proceso de parseo
	 */
	private StringBuffer sBuffer = null;

	/**
	 * Constructora por defecto
	 */
	public ParserSAXLista() {
	}

	/**
	 * Se encarga de crear el parser, el cual validar&aacute; el archivo
	 * indicado y lo procesar&aacute; mediante el mecanismo <i>SAX</i> de parseo
	 * de archivos <i>XML</i>. El parseo consiste en mapear las etiquetas del
	 * archivo a un objeto de tipo Playlist.
	 * 
	 * @param filename
	 *            La ruta del archivo a parsear.
	 * 
	 * @return La lista de reproducci&oacute;n.
	 * 
	 * @throws ReaderException
	 *             Aglutina una serie de excepciones relacionadas con la
	 *             apertura del archivo y el parseo en s&iacute;.
	 * 
	 * @see Playlist
	 * @see SAXParser
	 * @see SAXParserFactory
	 */
	public ArrayList<Integer> vParseFile(String filename)
			throws ReaderException {
		vIds = new ArrayList<Integer>();
		try {
			// Obtener una clase SAXParserFactory para obtener un SAXParser
			SAXParserFactory oSAXParserFactory = SAXParserFactory.newInstance();

			// Definimos las opciones de parseo: validar el xml
			oSAXParserFactory.setValidating(false);

			// Definimos las opciones de parseo: namespace no activado
			oSAXParserFactory.setNamespaceAware(false);

			// Obtener una clase SAXParser
			SAXParser oSAXParser = oSAXParserFactory.newSAXParser();

			// Empezar a parsear el archivo con esta clase para recoger los
			// eventos de parseo
			oSAXParser.parse(new File(filename), this);

		} catch (FactoryConfigurationError e) {
			throw new ReaderException(
					"ReaderException[FactoryConfigurationError]: "
							+ e.toString());
		} catch (ParserConfigurationException e) {
			throw new ReaderException(
					"ReaderException[ParserConfigurationException]: "
							+ e.toString());
		} catch (SAXException e) {
			throw new ReaderException("ReaderException[SAXException]: "
					+ e.toString());
		} catch (IOException e) {
			throw new ReaderException("ReaderException[IOException]: "
					+ e.toString());
		}
		return vIds;
	}

	/**
	 * <u>M&eacute;todo sobreescrito</u>. Muestra por consola el resultado de un
	 * warning en el parseo.
	 * 
	 * @see org.xml.sax.helpers.DefaultHandler#warning(org.xml.sax.SAXParseException)
	 */
	@Override
	public void warning(SAXParseException e) throws SAXException {
		System.out.println("Warning: ");
		printInfo(e);
	}

	/**
	 * <u>M&eacute;todo sobreescrito</u>. Muestra por consola el resultado de un
	 * error en el parseo.
	 * 
	 * @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException)
	 */
	@Override
	public void error(SAXParseException e) throws SAXException {
		System.out.println("Error: ");
		printInfo(e);
	}

	/**
	 * <u>M&eacute;todo sobreescrito</u>. Muestra por consola el resultado de un
	 * error fatal en el parseo.
	 * 
	 * @see org.xml.sax.helpers.DefaultHandler#fatalError(org.xml.sax.SAXParseException)
	 */
	@Override
	public void fatalError(SAXParseException e) throws SAXException {
		System.out.println("Fatal error: ");
		printInfo(e);
	}

	/**
	 * <u>M&eacute;todo sobreescrito</u>. Realiza las acciones pertinentes al
	 * encontrar una etiqueta de apertura. En este caso, simplemente muestra por
	 * consola el elemento de apertura parseado.
	 * 
	 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
	 *      java.lang.String, java.lang.String, org.xml.sax.Attributes)
	 * @see Playlist
	 */
	@Override
	public void startElement(String namespaceURI, String localName,
			String qualifiedName, Attributes attributes) throws SAXException {
		if ("".equals(localName))
			localName = qualifiedName;
		if (localName.equalsIgnoreCase("id")) {
			System.out.print("  ");
		}
		System.out.println("<" + localName + ">");
	}

	/**
	 * <u>M&eacute;todo sobreescrito</u>. Procesa la cadena de caracteres
	 * situada entre dos etiquetas del mismo tipo.
	 * 
	 * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
	 */
	@Override
	public void characters(char buf[], int offset, int len) throws SAXException {
		String s = new String(buf, offset, len);
		if (sBuffer == null)
			sBuffer = new StringBuffer(len);
		sBuffer.append(s.trim());
		System.out.println("\t" + sBuffer);
	}

	/**
	 * <u>M&eacute;todo sobreescrito</u>. Realiza las acciones pertinentes al
	 * encontrar una etiqueta de cierre. En este caso, almacena la cadena
	 * obtenida en {@link #characters(char[], int, int)} como un nuevo
	 * identificador de canci&oacute;n en una {@link Playlist}.
	 * 
	 * @see Biblioteca
	 * @see Cancion
	 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
	 *      java.lang.String, java.lang.String)
	 * @see Playlist
	 */
	@Override
	public void endElement(String uri, String localName, String qualifiedName)
			throws SAXException {
		if ("".equals(localName))
			localName = qualifiedName;

		if (localName.compareToIgnoreCase("id") == 0) {
			idActual = Integer.parseInt(sBuffer.toString());
			vIds.add(idActual);
		}

		if (localName.equalsIgnoreCase("id")) {
			System.out.print("  ");
		}
		System.out.println("</" + localName + ">");
		if (localName.equalsIgnoreCase("lista")) {
			System.out.println();
		}

		// Limpiamos sBuffer
		sBuffer = null;
	}

	/*--- MÉTODOS PRIVADOS ---*/
	private void printInfo(SAXParseException e) {
		System.out.println("\tPublic ID: " + e.getPublicId());
		System.out.println("\tSystem ID: " + e.getSystemId());
		System.out.println("\tN\u00famero de l\u00ednea: " + e.getLineNumber());
		System.out.println("\tN\u00famero de columna: " + e.getColumnNumber());
		System.out.println("\tMensaje: " + e.getMessage() + "\n");
	}
}